package fr.cephb.dbsnptools;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.util.HashSet;
import java.util.Set;

import com.sleepycat.bind.tuple.BooleanBinding;
import com.sleepycat.bind.tuple.StringBinding;
import com.sleepycat.je.DatabaseConfig;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.Environment;
import com.sleepycat.je.EnvironmentConfig;

import fr.cephb.berkeleydb.db.PrimaryDB;
import fr.cephb.io.IOUtils;
import fr.cephb.util.AbstractCmdLine;

public class FastaGrep
	extends AbstractCmdLine
	{
	private File dbHome=null;
	private Set<String> fileIdentifiers=new HashSet<String>();
	private Environment environment=null;
	private PrimaryDB<String, Boolean> name=null;
	private boolean case_sensible=true;
	private boolean inverse=false;
	
	private FastaGrep()
		{
		
		}
	
	private void open() throws DatabaseException,IOException
		{
		this.dbHome=IOUtils.createTempDir("_tmp_id",".dir",new File(System.getProperty("user.dir")));
		EnvironmentConfig envConfig= new EnvironmentConfig();
		envConfig.setAllowCreate(true);
		envConfig.setReadOnly(false);
		this.environment= new Environment(dbHome, envConfig);
		DatabaseConfig cfg= new DatabaseConfig();
		cfg.setAllowCreate(true);
		cfg.setReadOnly(false);
		this.name=new PrimaryDB<String, Boolean>(this.environment, null, "names", cfg, new StringBinding(), new BooleanBinding());
		}
	
	private void close()
		{
		if(this.name!=null)
			{
			this.name.close();
			}
		if(this.environment!=null)
			{
			this.environment.close();
			}
		if(this.dbHome!=null)
			{
			for(File f:this.dbHome.listFiles())
				{
				f.delete();
				}
			this.dbHome.delete();
			}
		}
	
	@Override
	protected int processArg(String[] args, int optind)
		{
		if(args[optind].equals("-i"))
			{
			this.case_sensible=false;
			return optind;
			}
		else if(args[optind].equals("-v"))
			{
			this.inverse=true;
			return optind;
			}
		else if(args[optind].equals("-f"))
			{
			String fname=args[++optind];
			this.fileIdentifiers.add(fname);
			return optind;
			}
		return super.processArg(args, optind);
		}
	@Override
	protected void usage(PrintStream out)
		{
		out.println("-v inverse selection");
		out.println("-i ignore case");
		out.println("-f <file> load file of identifiers");
		super.usage(out);
		out.println("(fasta files|stdin)");
		}
	
	private void loadIdentifiers(String fname)
		{
		BufferedReader r=null;
		try
			{
			r=IOUtils.openReader(fname);
			String line=null;
			while((line=r.readLine())!=null)
				{
				if(line.trim().isEmpty()) continue;
				if(!this.case_sensible) line=line.toLowerCase();
				this.name.put(null, line, true);
				}
			}
		catch (IOException e)
			{
			throw new RuntimeException(e);
			}
		}
	private void parse(BufferedReader r) throws IOException
		{
		boolean at_start=true;
		boolean in_seq=false;
		int c;
		StringBuilder name=new StringBuilder();
		while((c=r.read())!=-1)
			{
			if(at_start && c=='>')
				{
				name.setLength(0);
				while((c=r.read())!=-1)
					{
					if(c=='\n')
						{
						at_start=true;
						break;
						}
					name.append((char)c);
					}
				String s=(this.case_sensible?name.toString():name.toString().toLowerCase());
				in_seq= this.name.containsKey(null,
				  	s
				   );
				if(this.inverse) in_seq=!in_seq;
				if(in_seq)
					{
					System.out.println(">"+s);
					}
				continue;
				}
			else if(in_seq)
				{
				System.out.print((char)c);
				if(c=='\n')
					{
					at_start=true;
					}
				}
			else
				{
				if(c=='\n')
					{
					at_start=true;
					}
				}
			}
		}
	
	public static void main(String[] args)
		{
		FastaGrep app=null;
		try
			{
			app=new FastaGrep();
			int optind=app.processArgs(args);
			if(app.fileIdentifiers.isEmpty())
				{
				System.err.println("No files for identifiers");
				return;
				}
			
			app.open();
			
			for(String f:app.fileIdentifiers)
				{
				app.loadIdentifiers(f);
				}
			
            if(optind==args.length)
                    {
                    app.parse(new BufferedReader(new InputStreamReader(System.in)));
                    }
            else
                    {
                    while(optind< args.length)
                            {
                          	java.io.BufferedReader r= IOUtils.openReader(args[optind++]);
                            app.parse(r);
                            r.close();
                            }
                    }
			}
		catch (Exception e)
			{
			e.printStackTrace();
			}
		finally
			{
			if(app!=null) app.close();
			}
		}
	}
